/********************************************************************************
* Copyright (c) 2009, Matteo Pelati (matteo.pelati@gmail.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the <organization> nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Matteo Pelati ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**************************************************************************************/

#pragma once


#include <fstream>
#include <string>
#include <cstdlib>
#include <stdlib.h>
#include <stdio.h>
#include <vcclr.h>
#include "dbxml/DbXml.hpp"
#include "dbXmlLib.h"
#include "DbException.h"
#include "DbXmlException.h"
#include "DbXmlData.h"
#include "DbXmlDocument.h"
#include "DbXmlResults.h"
#include "DbXmlTransaction.h"
#include "DbXmlQueryContext.h"

using namespace DbXml;
using namespace System;
using namespace System::Collections::Generic;
using namespace std;

namespace BerkeleyDB { namespace Xml
{

	ref class DbXmlResults;

	public enum class DbXmlValueType
	{
		None = XmlValue::NONE,
		AnyURI = XmlValue::ANY_URI,
		Base64Binary = XmlValue::BASE_64_BINARY ,
		Boolean = XmlValue::BOOLEAN,
		Date = XmlValue::DATE,
		DateTime = XmlValue::DATE_TIME,
		DateTimeDuration = XmlValue::DAY_TIME_DURATION,
		Decimal = XmlValue::DECIMAL,
		Double = XmlValue::DOUBLE,
		Duration = XmlValue::DURATION,
		Float = XmlValue::FLOAT,
		Day = XmlValue::G_DAY,
		Month = XmlValue::G_MONTH,
		MonthDay = XmlValue::G_MONTH_DAY,
		Year = XmlValue::G_YEAR,
		YearMonth = XmlValue::G_YEAR_MONTH ,
		HexBinary = XmlValue::HEX_BINARY ,
		Notation = XmlValue::NOTATION ,
		QName = XmlValue::QNAME ,
		String = XmlValue::STRING ,
		Time = XmlValue::TIME ,
		YearMonthDuration = XmlValue::YEAR_MONTH_DURATION ,
		UntypedAtomic = XmlValue::UNTYPED_ATOMIC,
		Binary = XmlValue::BINARY
	};

	public enum class DbXmlNodeType  : short
	{
		Element = XmlValue::ELEMENT_NODE, 
		Attribute = XmlValue::ATTRIBUTE_NODE, 
		Text = XmlValue::TEXT_NODE, 
		Cdata = XmlValue::CDATA_SECTION_NODE, 
		EntityReference = XmlValue::ENTITY_REFERENCE_NODE, 
		Entity = XmlValue::ENTITY_NODE, 
		ProcessingInstruction = XmlValue::PROCESSING_INSTRUCTION_NODE, 
		Comment = XmlValue::COMMENT_NODE, 
		Document = XmlValue::DOCUMENT_NODE, 
		DocumentType = XmlValue::DOCUMENT_TYPE_NODE, 
		DocumentFragment = XmlValue::DOCUMENT_FRAGMENT_NODE, 
		Notation = XmlValue::NOTATION_NODE
	};

	public ref class DbXmlValue : DbGenericObject, public IDbXmlDatabaseItem
	{
	private:
		XmlValue* n_val;
		DbXmlTransaction^ currTxn;
		DbXmlQueryContext^ currCtx;
	internal:
		/*
		DbXmlValue(DbGenericObject^ parent, XmlValue* n_val, DbXmlTransaction^ txn, DbXmlQueryContext^ ctx) : DbGenericObject(parent)
		{
			this->n_val = n_val;
			this->currCtx = ctx;
			this->currTxn = txn;
		};
		*/

		DbXmlValue(DbGenericObject^ parent, XmlValue& n_val, DbXmlTransaction^ txn, DbXmlQueryContext^ ctx) : DbGenericObject(parent)
		{
			this->n_val = new XmlValue(n_val);
			this->currCtx = ctx;
			this->currTxn = txn;
		};
		XmlValue* GetInnerObject()
		{
			return n_val;
		};
	public:
		~DbXmlValue()
		{
			GC::SuppressFinalize(this);
			this->!DbXmlValue();
		}
		!DbXmlValue()
		{
			delete n_val;
		}
	public:
		virtual DbXmlDatabaseItemType GetDatabaseItemType()
		{
			return DbXmlDatabaseItemType::XmlNode;
		}

		DbXmlValue() : DbGenericObject(nullptr)
		{
			try
			{
				n_val = new XmlValue();
				this->currCtx = nullptr;
				this->currTxn = nullptr;
			}
			rethrowDbXmlException();
		}

		DbXmlValue(String^ name) : DbGenericObject(nullptr)
		{
			try
			{
				CLRSTR2STDSTR(s_name, name);
				n_val = new XmlValue(s_name);
				this->currCtx = nullptr;
				this->currTxn = nullptr;
			}
			rethrowDbXmlException();
		}

		DbXmlValue(double val) : DbGenericObject(nullptr)
		{
			try
			{
				n_val = new XmlValue(val);
				this->currCtx = nullptr;
				this->currTxn = nullptr;
			}
			rethrowDbXmlException();
		}

		DbXmlValue(bool val) : DbGenericObject(nullptr)
		{
			try
			{
				n_val = new XmlValue(val);
				this->currCtx = nullptr;
				this->currTxn = nullptr;
			}
			rethrowDbXmlException();
		}

		DbXmlValue(DbXmlValueType type, String^ name) : DbGenericObject(nullptr)
		{
			try
			{
				CLRSTR2STDSTR(s_name, name);
				n_val = new XmlValue((XmlValue::Type) type, s_name);
				this->currCtx = nullptr;
				this->currTxn = nullptr;
			}
			rethrowDbXmlException();
		}

		DbXmlValue(DbXmlValueType type, DbXmlData^ data) : DbGenericObject(nullptr)
		{
			try
			{
				data->Parent = this;
				n_val = new XmlValue((XmlValue::Type) type, *(data->GetInnerObject()));
				this->currCtx = nullptr;
				this->currTxn = nullptr;
			}
			rethrowDbXmlException();
		}

		DbXmlValue(DbXmlDocument^ doc) : DbGenericObject(doc)
		{
			try
			{
				n_val = new XmlValue(*(doc->GetInnerObject()));
				this->currCtx = nullptr;
				this->currTxn = nullptr;
			}
			rethrowDbXmlException();
		}

		// Properties

		PROPERTY_RET_ENUM_GET(Type,, Type, DbXmlValueType, XmlValue::Type);
		PROPERTY_RET_CSTRING_GET(TypeURI,, TypeURI);
		PROPERTY_RET_CSTRING_GET(TypeName,, TypeName);
		PROPERTY_RET_CSTRING_GET(NodeHandle,, NodeHandle);
		PROPERTY_RET_CSTRING_GET(NodeName,, NodeName);
		PROPERTY_RET_CSTRING_GET(NodeValue,, NodeValue);
		PROPERTY_RET_CSTRING_GET(NamespaceURI,, NamespaceURI);
		PROPERTY_RET_CSTRING_GET(Prefix,, Prefix);
		PROPERTY_RET_CSTRING_GET(LocalName,, LocalName);
		PROPERTY_RET_ENUM_GET(NodeType,, NodeType, DbXmlNodeType, short);
		PROPERTY_RET_XMLVAL_GET(ParentNode, DbXmlValue, XmlValue,, ParentNode, currTxn, currCtx);
		PROPERTY_RET_XMLVAL_GET(FirstChild, DbXmlValue, XmlValue,, FirstChild, currTxn, currCtx);
		PROPERTY_RET_XMLVAL_GET(LastChild, DbXmlValue, XmlValue,, LastChild, currTxn, currCtx);
		PROPERTY_RET_XMLVAL_GET(PreviousSibling, DbXmlValue, XmlValue,, PreviousSibling, currTxn, currCtx);
		PROPERTY_RET_XMLVAL_GET(NextSibling, DbXmlValue, XmlValue,, NextSibling, currTxn, currCtx);
		PROPERTY_RET_XMLVAL_GET(OwnerElement, DbXmlValue, XmlValue,, OwnerElement, currTxn, currCtx);
		PROPERTY_RET_OBJ_GET(Attributes, DbXmlValueCollection, XmlResults,, Attributes, currTxn, currCtx); 

		virtual bool IsBinary()
		{
			try { return GetInnerObject()->isBinary(); } rethrowDbXmlException();
		}

		virtual bool IsBoolean()
		{
			try { return GetInnerObject()->isBoolean(); } rethrowDbXmlException();
		}

		virtual bool IsNode()
		{
			try { return GetInnerObject()->isNode(); } rethrowDbXmlException();
		}

		virtual bool IsNull()
		{
			try { return GetInnerObject()->isNull(); } rethrowDbXmlException();
		}

		virtual bool IsNumber()
		{
			try { return GetInnerObject()->isNumber(); } rethrowDbXmlException();
		}

		virtual bool IsString()
		{
			try { return GetInnerObject()->isBoolean(); } rethrowDbXmlException();
		}

		virtual bool IsType(DbXmlValueType type)
		{
			try { return GetInnerObject()->isType((XmlValue::Type) type); } rethrowDbXmlException();
		}

		bool operator==(DbXmlValue^ val)
		{ 
			//if (val === nullptr) return false;
			try { return (*(this->GetInnerObject()) == *(val->GetInnerObject())); } rethrowDbXmlException();
		}

		double asNumber()
		{
			try { return GetInnerObject()->asNumber(); } rethrowDbXmlException();
		}

		String^ asString()
		{
			try { return gcnew String(GetInnerObject()->asString().c_str()); } rethrowDbXmlException();
		}

		DbXmlData^ asBinary()
		{
			try { return gcnew DbXmlData(this, GetInnerObject()->asBinary()); } rethrowDbXmlException();
		}

		bool asBoolean()
		{
			try { return GetInnerObject()->asBoolean(); } rethrowDbXmlException();
		}

		DbXmlDocument^ asDocument()
		{
			try 
			{ 
				return gcnew DbXmlDocument(this, (const XmlDocument) GetInnerObject()->asDocument());
			} 
			rethrowDbXmlException();
		}

		bool Equals(DbXmlValue^ value)
		{
			try { return GetInnerObject()->equals(*(value->GetInnerObject())); } rethrowDbXmlException();
		}

		IEnumerable<DbXmlValue^>^ SelectNodes(String^ query);
		DbXmlValue^ SelectNode(String^ query);
		bool HasAttribute(String^ name);
		String^ GetAttribute(String^ name);
	



	};

}}
